/*
 * Copyright (c) 2023 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#ifndef CHIP_SMC_H
#define CHIP_SMC_H

#include "internal/bus/bus_base/bus.h"
#include "internal/chip/chip_base/chip.h"
#include <mc/engine.h>
#include <memory>
#include <map>

namespace dev {

class MC_API chip_smc : public chip_base {
public:
    bool     init(mc::mutable_dict& csr_object);
    int32_t bit_read(uint32_t offset, uint8_t length, uint32_t mask,
        std::vector<uint8_t>& in_buffer, std::vector<uint8_t>& out_buffer,
        std::string& error) override;
    int32_t bit_write(uint32_t offset, uint8_t length, uint32_t mask,
         const std::vector<uint8_t>& in_buffer,
         std::vector<uint8_t>&       out_buffer,
         std::string& error) override;
    int32_t block_read(uint32_t offset, uint32_t length, std::vector<uint8_t>& in_buffer,
          std::vector<uint8_t>& out_buffer, std::string& error) override;
    int32_t block_write(uint32_t offset, const std::vector<uint8_t>& in_buffer,
           std::vector<uint8_t>& out_buffer, std::string& error) override;
    int32_t block_write_read(uint32_t offset, const std::vector<uint8_t>& indata,
                uint32_t read_length, std::vector<uint8_t>& out_buffer,
                std::string& error) override;

protected:
    uint32_t forward_read(uint32_t length, uint8_t* data_out);
    uint32_t forward_write(uint32_t offset, uint32_t length, uint8_t* buffer);
    int32_t forward_by_front_chip(ACCESS_DATA_S& data, const std::string& class_name, uint8_t bus_id) override;
    const char* get_class_name() const override;

    uint32_t read(uint32_t offset, uint32_t length, std::vector<uint8_t>& buffer, std::string& error);
    uint32_t write(uint32_t offset, uint32_t length, const uint8_t* buffer, std::string& error);

private:
    uint32_t normal_read(uint32_t offset, uint32_t buffer_length, uint8_t* buffer, std::string& error);
    uint32_t normal_write(uint32_t offset, uint32_t length, const uint8_t* buffer, uint8_t command_code, std::string& error);
    uint32_t read_data(uint32_t read_length, uint8_t* out_buffer, uint8_t command_code, std::string& error);

    void get_length_from_map(uint32_t offset, uint32_t& record_length);
    void update_length_to_map(uint32_t offset, uint32_t length);

    uint32_t read_from_hardware(uint32_t opcode, uint32_t out_length, uint8_t* out_buffer, std::string& error);
    uint32_t calc_forward_opcode(uint8_t addr, uint32_t length, uint8_t chip_type, uint8_t bus_id);

private:
    uint8_t m_channel_num;

    enum SMC_COMPLETION_CODE {
        SMC_CC_SUCCESS = 0x00,
        SMC_CC_NOT_SUPPORT,
        SMC_CC_NOT_READY,
        SMC_CC_PARA_ERR,
        SMC_CC_INTERNAL_ERR,
    };

    enum SMC_COMMAND_CODE {
        SMC_WRITE_OPCODE = 0x20,
        SMC_READ_BUFFER = 0x21,
        SMC_WRITE_OPCODE_WITH_DATA = 0x22,
        SMC_WRITE_OPCODE_FORWARD = 0x30,
        SMC_READ_BUFFER_FORWARD = 0x31,
        SMC_WRITE_WITH_DATA_FORWARD = 0x32,
    };

    std::map<uint32_t, uint32_t> m_opcode_length_map;
};

} // namespace dev

#endif // CHIP_SMC_H